﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Kinect;
using BicycleTrafficSimulator.CarBehaviour;
using System.Threading;

namespace BicycleTrafficSimulator.DataModel
{

    public class Car : AbstractModel,IGameObject
    {

        LinearBehaviour behaviour;
        private WaypointList waypointList;
        private Queue<Vector3> waypoints = new Queue<Vector3>();
        private Vector3 previousWaypoint;
        private BoundingSphere boundingSphere;

        public Queue<Vector3> Waypoints
        {
            get { return waypoints; }
            set { waypoints = value; }
        }


        public BoundingSphere BoundingSphere
        {
            get { return boundingSphere; }
            set { boundingSphere = value; }
        }

        public Car(String level)
        {
            this.Position = new Vector3(2.8f, -0.05f, -29.4f);
            this.Rotation = 3.1f;
            this.Scale = new Vector3(0.0014f, 0.0014f, 0.0014f);
            waypointList = new WaypointList(level);
            this.waypoints = waypointList.Waypoints;
            previousWaypoint = new Vector3(2.8f, -0.05f, -20f);
            
            if (level == "easy")
                this.maxMoveSpeed = 1;
            else
                this.maxMoveSpeed = 2;
            behaviour = new LinearBehaviour(this);
        }


        const float atDestinationLimit = 0.1f;

        public float MaxMoveSpeed
        {
            get { return maxMoveSpeed; }
        }
        float maxMoveSpeed;
        

   
        public Vector3 Direction
        {
            get { return direction; }
            set { direction = value; }
        }
        protected Vector3 direction;

        public float MoveSpeed
        {
            get { return moveSpeed; }
            set { moveSpeed = value; }
        }
        protected float moveSpeed;

        public float DistanceToDestination
        {
            get { return Vector3.Distance(Position, waypoints.Peek()); }
        }

        public bool AtDestination
        {
            get { return DistanceToDestination < atDestinationLimit; }
        }

        public void LoadContent(ContentManager content, String modelName){
            this.Model = content.Load<Model>(modelName);
        
        }
   
        public void Update(GameTime gameTime)
        {
            float elapsedTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
            BoundingSphere = new BoundingSphere(this.Position, 0.5f);

            // If we have any waypoints, the first one on the list is where 
            // we want to go
            if (waypoints.Count > 0)
            {
                if (AtDestination)
                {
                    // If we’re at the destination and there is at least one 
                    // waypoint in the list, get rid of the first one since we’re 
                    // there now
                    previousWaypoint = waypoints.Peek();
                    waypoints.Enqueue(previousWaypoint);
                    waypoints.Dequeue();
                    if (waypoints.Count > 0)
                    {
                        if (previousWaypoint.X != waypoints.Peek().X)
                        {
                            if (previousWaypoint.X < waypoints.Peek().X)
                                this.Rotation = 4.7f;
                            else
                                this.Rotation = 1.6f;
                        }
                        if (previousWaypoint.Z != waypoints.Peek().Z)
                        {
                            if (previousWaypoint.Z < waypoints.Peek().Z)
                                this.Rotation = 3.15f;
                            else
                                this.Rotation = 0f;
                        }
                    }
                }
                else
                {      
                    // If we’re not at the destination, call Update on our 
                    // behaviour and then move

                    if (behaviour != null)
                    {
                        behaviour.Update(gameTime);
                    }
                    this.Position = this.Position + (Direction * MoveSpeed * elapsedTime);   
                }
            }
        }

        public void Draw(Matrix viewMatrix, Matrix projectionMatrix)
        {
            // Copy any parent transforms.
            Matrix[] transforms = new Matrix[this.Model.Bones.Count];
            this.Model.CopyAbsoluteBoneTransformsTo(transforms);

            // Draw the model. A model can have multiple meshes, so loop.
            foreach (ModelMesh mesh in this.Model.Meshes)
            {
                // This is where the mesh orientation is set, as well 
                // as our camera and projection.
                foreach (BasicEffect effect in mesh.Effects)
                {
                    effect.EnableDefaultLighting();
                    effect.World = transforms[mesh.ParentBone.Index] *
                        Matrix.CreateScale(this.Scale.X, this.Scale.Y, this.Scale.Z) * Matrix.CreateRotationY(MathHelper.Pi) *
                        Matrix.CreateRotationY(this.Rotation)
                        * Matrix.CreateTranslation(this.Position);
                    effect.View = viewMatrix;
                    effect.Projection = projectionMatrix;
                }
                // Draw the mesh, using the effects set above.
                mesh.Draw();
            }
        }

    }
}
